/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package facilitator;

import core.CalculationException;
import core.DmuList;
import core.Optimization;
import core.Optimizer;
import core.PdmOptimization;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import util.Util;

/**
 *
 * @author Niki
 */
public class Bisection_1 {
private Queue<Problem> problems = new ConcurrentLinkedQueue<Problem>();
private ArrayList<PdmOptimization> transitions;
private Solver solver;
private double uBound =-1;
private double precision = 0.1E-10;

    public Bisection_1(DmuList dmuList, int objectiveDmu, double[] urmWeights, SolverModell solver, double precision){
        this.transitions = new ArrayList<PdmOptimization>();
        this.precision = precision;
        switch(solver){
            case Heuristik:
                this.solver = new HeuristikSolver(dmuList, objectiveDmu, urmWeights);
                break;
            case Global:
                this.solver = new GlobalSolver(dmuList, objectiveDmu, urmWeights);
                break;
        }
    }
    private double getAlphaUBound() throws CalculationException{
        if(this.uBound == -1){
            double alpha = 0.5;
            PdmOptimization curr = solver.getOptimization(alpha);
            while(curr.getProfileDistance() > 0.1E-10){
                alpha *= 2;
                curr = solver.getOptimization(alpha);
            }
            this.uBound = curr.getAlpha();
        }
        return this.uBound;
    }
    public List<PdmOptimization> getTransitions() throws CalculationException{
        return this.getTransitions(this.getAlphaUBound());
    }
    public List<PdmOptimization> getTransitions(double alphaUpperBound) throws CalculationException{
        PdmOptimization lowBo = solver.getOptimization(0);
        PdmOptimization uppBo = solver.getOptimization(alphaUpperBound);
        //Util.print("UpperBound: " + alphaUpperBound  + " ProfileDistance: " +uppBo.getProfileDistance());
        transitions.add(lowBo);
        //transitions.add(uppBo);
        problems.add(new Problem(lowBo, uppBo));
        while(!problems.isEmpty()){
            solveProblem(problems.remove());
        }
        Collections.sort(transitions);
        //Util.print("size " + transitions.size());
        return transitions;
    }

    /**
     * 2 cases:
     * lowbo and upbo are identical => problem solved
     * upBo <> lowbo => 2 new problems (lowbo - upbow/2, upbow/2 - upbow)
     * @param pop
     */
    private void solveProblem(Problem prob) {
        //Util.print("Problem: " + prob.lowBound.getAlpha() + " ||| " + prob.uppBound.getAlpha());
        try {
            /*if(prob.uppBound.getAlpha()-prob.lowBound.getAlpha() < 0.000000001){
                transitions.add(prob.uppBound);
                Util.print("Exiting");
                return;
            }*/
            if(prob.uppBound.getAlpha() - prob.lowBound.getAlpha() > this.precision){
                if(Double.compare(prob.lowBound.getProfileDistance(), prob.uppBound.getProfileDistance())!= 0){
                    //Util.print("LowBo " + prob.lowBound.getProfileDistance() + " - UppBo " + prob.uppBound.getProfileDistance());
                    PdmOptimization midOpt = solver.getOptimization((prob.lowBound.getAlpha() + prob.uppBound.getAlpha())/2);
                    if(Math.abs(prob.lowBound.getProfileDistance()-midOpt.getProfileDistance()) >= precision){
                        //Util.print("New Lower Problem: " + prob.lowBound.getAlpha() + " - " + midOpt.getAlpha());
                        //Util.print("          Problem: " + prob.lowBound.getProfileDistance() + " - " + midOpt.getProfileDistance());
                        problems.add(new Problem(prob.lowBound, midOpt));
                    }
                    if(Math.abs(midOpt.getProfileDistance() - prob.uppBound.getProfileDistance()) >= precision){
                        //Util.print("New Upper Problem: " + midOpt.getAlpha() +" - " + prob.uppBound.getAlpha());
                        //Util.print("          Problem: " + prob.lowBound.getProfileDistance() + " - " + midOpt.getProfileDistance());
                        problems.add(new Problem(midOpt, prob.uppBound));
                    }
                }
            }
            else{
                //Util.print("Precision reached");
                transitions.add(prob.uppBound);
            }
        } catch (CalculationException ex) {
            Logger.getLogger(Bisection_1.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    class Problem{
        public PdmOptimization lowBound;
        public PdmOptimization uppBound;

        Problem(PdmOptimization lowBound, PdmOptimization uppBound) {
            this.lowBound = lowBound;
            this.uppBound = uppBound;
        }
    }
    public enum SolverModell{
        Heuristik, Global
    }
}
